#include "keyword.hxx"
#include <assert.h>
#include <sstream>
#include "utility.hxx"

using namespace std;

Keyword::Keyword(const string& name)
:m_Name(name),
segments(),
spec()
{
	Parse();
}
bool Keyword::IsResultQualified(const string& result) const
{
	assert(segments.size() > 0);

	// Find all seg in the result one by one.
	size_t pos = 0;
	size_t newPos;
	std::vector<KeywordSeg>::const_iterator iterSeg = segments.begin();
	while (iterSeg != segments.end())
	{
		if (std::string::npos == (newPos = result.find(iterSeg->seg, pos)))
			return false;

		pos = newPos + 1;
		++iterSeg;
	}

	return true;
}
// Find into Name if 3 same alnums.
bool Keyword::Is3TongTargeted(void) const
{
	char c = 0;
	int sameNum = 0;

	for (size_t i=0; i<m_Name.length(); i++)
	{
		if (isalnum(m_Name[i]))
		{
			if (c != 0 && c != m_Name[i])
				return false;
			c = m_Name[i];
			++sameNum;
		}
	}

	return (sameNum >= 3);
}
const string Keyword::GetSpec(void) const
{
	return spec;
}
const std::vector<std::string> Keyword::GetExtSpecs(void) const
{
	// move the valid joined chars to create.
	std::vector<std::string> results;

	std::string specStr = spec;
	Utility::replace(specStr, ' ', 0);

	int spaceNum = KeywordLength-specStr.length();
	for (int i=0;i<=spaceNum; i++)
	{
		string extSpec = Utility::spaceStr(i) + specStr + Utility::spaceStr(spaceNum - i);
		assert(extSpec.length() == KeywordLength);
		
		results.push_back(extSpec);
	}

	/*std::string rawSpec = spec.replace(*/
	return results;
}
/**
 * 0. vadality check by length;
 * 0.1 preprocess;
 * 1. create keyword segs;
 * 2. find a seg that is fixed and with valid length >= 2;
 * 3. or find two segs that is fixed, with sum of the valid lengths >= 2;
 * 4. with the fixed part, create the spec keyword;
 **/
bool Keyword::Parse()
{
	string No = Name;
	string checkStr = No;
	string ::iterator iter = checkStr.begin();
	while (iter != checkStr.end())
	{
		if (*iter == '*' || *iter == '^' || *iter == '$')
			iter = checkStr.erase(iter);
		else
			iter++;
	}

	string numStr = No;
	numStr = Utility::replace(numStr, '.', ' ');
	for (size_t i = numStr.length(); i > 0; --i)
	{
		/* remove duplicate *. */
		if (numStr[i-1] == '*' && i < numStr.length() && numStr[i] == '*')
			numStr.erase(i, 1);
	}
	
	/* split the keyword into KeywordSegs. */
	size_t pos = 0;
	size_t newPos = 0;
	std::vector<std::string> segs;
	Utility::split(numStr, '*', segs);
	for (size_t i=0; i<segs.size(); i++)
	{
		Keyword::KeywordSeg seg(segs[i], string::npos);
		segments.push_back(seg);
	}

	/* any start or end fixed, like ^ and $. */
	if (segments.front().seg[0] == '^')
	{
		segments.front().seg = segments.front().seg.substr(1);
		segments.front().startPos = 0;
	}
	else if (segments.front().seg.length() == KeywordLength)
	{
		segments.front().startPos = 0;
		assert(segments.size() == 1);
	}
	if (segments.back().seg[segments.back().seg.length()-1] == '$')
	{
		segments.back().seg = segments.back().seg.substr(0, segments.back().seg.length()-1);
		segments.back().startPos = KeywordLength - segments.back().seg.length();
		assert(segments.back().startPos >= 0);
	}

	string result;
	for (size_t i = 0; i<segments.size(); i++)
	{
		KeywordSeg seg = segments[i];
		assert(seg.ValidLength()>0);

		if (seg.startPos != string::npos && seg.ValidLength() >= 2)
		{
			Compose(result, KeywordLength, seg);
			spec = result;
			return true;
		}
	}

	KeywordSeg seg1("", string::npos);
	KeywordSeg seg2("", string::npos);
	for (size_t i = 0; i<segments.size(); i++)
	{
		KeywordSeg varSeg = segments[i];
		if (varSeg.startPos != string::npos)
		{
			if (seg1.startPos == string::npos)
				seg1 = varSeg;
			else
				seg2 = varSeg;
		}

		if (seg1.startPos != string::npos && seg2.startPos != string::npos)
		{
			Compose(result, KeywordLength, seg1, seg2);
			spec = result;
			return true;
		}
	}

	return false;
}
bool Keyword::Compose(std::string& result, size_t keywordLen, const KeywordSeg& seg1, const KeywordSeg& seg2)
{
	result.clear();

	if (seg2.startPos != string::npos)
	{
		assert(seg1.startPos + seg1.seg.length() <= seg2.startPos);
		assert(seg2.startPos + seg2.seg.length() <= keywordLen);
	}
	else
	{
		assert(seg1.startPos + seg1.seg.length() <= keywordLen);
	}

	for (size_t i=0; i<seg1.startPos; i++)
	{
		result.append(" ");
	}
	result.append(seg1.seg);

	if (seg2.startPos != string::npos)
	{
		for (size_t i=0; i<(seg2.startPos-seg1.startPos - seg1.seg.length()); i++)
		{
			result.append(" ");
		}
		result.append(seg2.seg);
	}

	for (size_t i=0; i<keywordLen-result.length(); i++)
	{
		result.append(" ");
	}

	return result.length() == keywordLen;
}